/* === LICENSE INFO ===
 * This source file is subject to the GPLv3 license that is bundled with this
 * package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl.txt
 * === end of LICENSE INFO === */

/*==============================================================================
  File Name   : LogWriterManager.cpp
  Description : Definition for LogWriterManager class in GLog module.

  Created on  : 2009-10-01
  Updated on  : 2011-01-30
  Updated by  : Lee Shiou Ming

  Cautions    : (important notes)
  ============================================================================*/

//------------------------------------------------------------------------------
//-- INCLUDES & DEFINES
//------------------------------------------------------------------------------
#include "LogWriterManager.hpp"
#include "LogScreenWriter.hpp"
#include "LogFileWriter.hpp"
#include <iostream>

using namespace gUtil::GLog;


//------------------------------------------------------------------------------
//-- INITIALIZATIONS
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
//-- FUNCTION IMPLEMENTATIONS
//------------------------------------------------------------------------------
LogWriterManager::LogWriterManager()
:
    m_logFilePath(""),
    m_logRotationSize(0),
    m_logRotationInterval(TIME_INTERVAL_NONE),
    m_strand(m_ioService),
    m_isAsyncLoggingStarted(false),
    m_isLoggingStopRequested(false),
    m_asyncIdleDuration(DEFAULT_ASYNC_LOGGING_IDLE)
{
}


LogWriterManager::~LogWriterManager()
{
    try
    {
        if (m_isAsyncLoggingStarted)
        {
            m_isLoggingStopRequested = true;
            m_loggingThread.join();
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "[ERROR] ~LogWriterManager(): " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "[ERROR] ~LogWriterManager(): Unknown exception detected."
                << std::endl;
    }
}


bool LogWriterManager::setLogFilePath(const std::string& _logFilePath)
{
    bool returnValue = true;

    m_logFilePath = _logFilePath;

    return returnValue;
}

bool LogWriterManager::setLogRotationSize(unsigned int _logRotationSize)
{
    bool returnValue = true;

    m_logRotationSize = _logRotationSize;

    return returnValue;
}


bool LogWriterManager::setLogRotationInterval(TimeInterval _logRotationInterval)
{
    bool returnValue = true;

    m_logRotationInterval = _logRotationInterval;

    return returnValue;
}


bool LogWriterManager::createLogWriter(const LogOutputType _enabledOutput)
{
    bool returnValue = true;
    LogWriterPtr logWriter;

    switch (_enabledOutput)
    {
        case OUTPUT_FILE:
            logWriter = LogWriterPtr(new LogFileWriter(
                    m_logFilePath, m_logRotationSize, m_logRotationInterval));
            m_logWriters.push_back(logWriter);
            break;
        case OUTPUT_SCREEN:
            logWriter = LogWriterPtr(new LogScreenWriter());
            m_logWriters.push_back(logWriter);
            break;
        default:
            std::cerr << "Invalid log output type [" << _enabledOutput <<
                "]. Discarded." << std::endl;
            returnValue = false;
            break;
    }

    return returnValue;
}


bool LogWriterManager::startAsyncLogging(const unsigned int _idleDuration)
{
    bool returnValue = false;

    try
    {
        if (_idleDuration > 0)
        {
            m_asyncIdleDuration = _idleDuration;
        }

        if (!m_isAsyncLoggingStarted)
        {
            // TODO[1]: Investigate the warning message for Boost.Thread
            //          whenever optimization been turn on for Win32_Release.
            m_loggingThread = boost::thread(
                    boost::bind(&LogWriterManager::loggingThreadFunc, this));

            m_isAsyncLoggingStarted = true;
            returnValue = true;
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "[ERROR] startAsyncLogging(): " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "[ERROR] startAsyncLogging(): Unknown exception detected."
                << std::endl;
    }

    return returnValue;
}


void LogWriterManager::appendLog(const std::string& _logMessage,
        const std::string& _logLevelString)
{
    GDateTime::ptime now = GDateTime::getCurrentTime();

    std::vector<LogWriterPtr>::iterator logWritersItr;
    for (logWritersItr = m_logWriters.begin();
            logWritersItr != m_logWriters.end();
            ++logWritersItr)
    {
        if (m_isAsyncLoggingStarted)
        {
            // Post a handler to I/O service to be execute asynchronously.
            m_ioService.post(m_strand.wrap(boost::bind(&LogWriter::append,
                    (*logWritersItr), _logMessage, _logLevelString, now)));
        }
        else
        {
            // Logging synchronously.
            (*logWritersItr)->append(_logMessage, _logLevelString, now);
        }
    }
}


void* LogWriterManager::loggingThreadFunc()
{
    int returnValue = 1;

    try
    {
        // TODO: Try to decouple boost date_time from loggingThreadFunc().
        boost::posix_time::seconds idleDuration(m_asyncIdleDuration);

        // This exit flag will be set in class's destructor during program's
        // termination. Didn't use mutex to synchronize access to this flag for
        // performance consideration. However this may delays the program shut
        // down by one more loop.
        while (!m_isLoggingStopRequested)
        {
            // Blocked for unfinished job, or execute the completion handlers
            // that currently in queue. In this case handlers are the actual log
            // writing.
            m_ioService.run();

            // Leave thread idle for a while if nothing to do. Might change to
            // wait/notify approach if sleep() method has performance issue,
            // i.e. if the actual usage puts this thread idle most of the time.
            boost::this_thread::sleep(idleDuration);
        }

        // In case there is any unfinished logging job in queue while exiting.
        // Comment out next line if no need to clear all log messages in queue.
        m_ioService.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "[ERROR] loggingThreadFunc(): " << e.what() << std::endl;
        returnValue = 0;
    }
    catch (...)
    {
        std::cerr << "[ERROR] loggingThreadFunc(): Unknown exception detected."
                << std::endl;
        returnValue = 0;
    }

    return reinterpret_cast<void*>(returnValue);
}
